﻿using System;
using System.ComponentModel.Design;
using Microsoft.VisualStudio.Shell;

namespace DiveDeeper.VsxLibrary.Core
{
  // ====================================================================================
  /// <summary>
  /// This abstract class is intended to be the base class for simple menu command 
  /// handlers.
  /// </summary>
  /// <remarks>
  /// A simplemenu command handler contains methods to execute the command or respond to
  /// the status query or change events.
  /// </remarks>
  // ====================================================================================
  public abstract class MenuCommandHandler
  {
    #region Private fields

    private readonly Package _Package;
    private readonly CommandID _CommandId;
    private OleMenuCommand _MenuCommand;

    #endregion

    #region Lifecycle methods

    // ------------------------------------------------------------------------------
    /// <summary>
    /// Creates an instance of this menu command handler class.
    /// </summary>
    /// <param name="package">Package owning the menu command.</param>
    /// <remarks>
    /// This constructor reads the <see cref="CommandIDAttribute"/> used to decorate
    /// this class in order toobtan command ID information.
    /// </remarks>
    // ------------------------------------------------------------------------------
    protected internal MenuCommandHandler(Package package)
    {
      _Package = package;
      foreach (object attr in GetType().GetCustomAttributes(false))
      {
        CommandIDAttribute idAttr = attr as CommandIDAttribute;
        if (idAttr != null)
        {
          _CommandId = new CommandID(idAttr.Guid, (int)idAttr.Command);
        }
      }
    }

    #endregion

    #region Public properties

    // ------------------------------------------------------------------------------
    /// <summary>
    /// Gets the CommandID belonging to the menu command.
    /// </summary>
    // ------------------------------------------------------------------------------
    public CommandID CommandId
    {
      get { return _CommandId; }
    }

    // ------------------------------------------------------------------------------
    /// <summary>
    /// Gets the package owning this menu command.
    /// </summary>
    // ------------------------------------------------------------------------------
    protected Package Package
    {
      get { return _Package; }
    }

    // ------------------------------------------------------------------------------
    /// <summary>
    /// Gets the service provider belonging to the owner package.
    /// </summary>
    // ------------------------------------------------------------------------------
    protected IServiceProvider ServiceProvider
    {
      get { return _Package; }
    }

    // ------------------------------------------------------------------------------
    /// <summary>
    /// Gets the OleMenuCommand instance belonging to this instance.
    /// </summary>
    // ------------------------------------------------------------------------------
    protected OleMenuCommand MenuCommand
    {
      get { return _MenuCommand; }
    }

    // ------------------------------------------------------------------------------
    /// <summary>
    /// Gets the flag indicating if the menu command has already bound to a menu item.
    /// </summary>
    // ------------------------------------------------------------------------------
    public bool IsBound
    {
      get { return _MenuCommand != null; }
    }

    #endregion

    #region Methods to override

    // ------------------------------------------------------------------------------
    /// <summary>
    /// Override this method to define how the command should be executed.
    /// </summary>
    /// <param name="command">OleMenuCommand instance</param>
    // ------------------------------------------------------------------------------
    protected virtual void OnExecute(OleMenuCommand command)
    {
    }

    // ------------------------------------------------------------------------------
    /// <summary>
    /// Override this method to define how the command status should be queried.
    /// </summary>
    /// <param name="command">OleMenuCommand instance</param>
    // ------------------------------------------------------------------------------
    protected virtual void OnQueryStatus(OleMenuCommand command)
    {
    }

    // ------------------------------------------------------------------------------
    /// <summary>
    /// Override this method to define how to respond command property changes.
    /// </summary>
    /// <param name="command">OleMenuCommand instance</param>
    // ------------------------------------------------------------------------------
    protected virtual void OnChange(OleMenuCommand command)
    {
    }

    #endregion

    #region Internal methods

    // ------------------------------------------------------------------------------
    /// <summary>
    /// Binds the command to the related menu and toolbar items.
    /// </summary>
    // ------------------------------------------------------------------------------
    public void Bind()
    {
      if (_Package == null) return;
      OleMenuCommandService mcs =
       ServiceProvider.GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
      if (mcs == null) return;

      _MenuCommand = new OleMenuCommand(
        ExecuteMenuCommandCallback,
        ChangeCallback,
        BeforeStatusQueryCallback,
        _CommandId);
      mcs.AddCommand(_MenuCommand);
    }

    #endregion

    #region Private methods

    // ------------------------------------------------------------------------------
    /// <summary>
    /// Callback method called when the command is to be executed.
    /// </summary>
    // ------------------------------------------------------------------------------
    private void ExecuteMenuCommandCallback(object sender, EventArgs e)
    {
      OleMenuCommand command = sender as OleMenuCommand;
      if (command != null) OnExecute(command);
    }

    // ------------------------------------------------------------------------------
    /// <summary>
    /// Callback method called when the command is about to be changed.
    /// </summary>
    // ------------------------------------------------------------------------------
    private void ChangeCallback(object sender, EventArgs e)
    {
      OleMenuCommand command = sender as OleMenuCommand;
      if (command != null) OnChange(command);
    }

    // ------------------------------------------------------------------------------
    /// <summary>
    /// Callback method called when the command status is queried.
    /// </summary>
    // ------------------------------------------------------------------------------
    private void BeforeStatusQueryCallback(object sender, EventArgs e)
    {
      OleMenuCommand command = sender as OleMenuCommand;
      if (command != null) OnQueryStatus(command);
    }

    #endregion
  }
}
